/*
* 710605420@qq.com
*/

#pragma once
#include <ctime>
#include <functional>
#include <boost/coroutine2/all.hpp>
#include "CoroutineMathine.h"

namespace BoostCorountine2
{
	using TYPE_INFO = void*;

	template <typename T> TYPE_INFO _TYPEID_()
	{
		return reinterpret_cast<TYPE_INFO>(&_TYPEID_<T>);
	}

#define TYPEID(T) _TYPEID_<T>()

	class YieldInstruction
	{
		friend class CoroutineMathine;
	public:
		YieldInstruction() = delete;

	private:
		TYPE_INFO Type;

	protected:
		YieldInstruction(TYPE_INFO InType)
			: Type(InType)
		{
		}

		virtual ~YieldInstruction()
		{
		}

		virtual void Cancel()
		{
		}

		virtual bool IsAlive()
		{
			return true;
		}
	};

	class WaitForSeconds : public YieldInstruction
	{
	private:
		float Time;

		float Now()
		{
			return std::clock() / static_cast<float>(CLOCKS_PER_SEC) ;
		}

	public:
		WaitForSeconds(float Seconds)
			: YieldInstruction(TYPEID(WaitForSeconds))
			, Time(Now() + Seconds)
		{
		}

	protected:
		virtual void Cancel() override
		{
			Time = Now();
		}

		virtual bool IsAlive() override
		{
			float sc = Now();
			bool val = Time >= sc;
			return val;
		}
	};

	class Break : public YieldInstruction
	{
	public:
		Break()
			: YieldInstruction(TYPEID(Break))
		{
		}

		virtual void Cancel()
		{
		}

		virtual bool IsAlive()
		{
			return false;
		}
	};
	using CoroBoost = boost::coroutines2::coroutine<class YieldInstruction*>;
	using CoroPull = CoroBoost::pull_type;
	using CoroPush = CoroBoost::push_type;
	using Enumerator = std::function<void(CoroPush&)>;

	class Coroutine : public YieldInstruction
	{
		friend class CoroutineMathine;
	private:
		CoroPull Current;
		Coroutine* MoveNext;
		bool bIsCancel;
	private:
		Coroutine(CoroPull& InPull)
			: YieldInstruction(TYPEID(Coroutine))
			, Current(std::move(InPull))
			, MoveNext(nullptr)
			, bIsCancel(false)
		{
		}


	protected:
		virtual void Cancel() override
		{
			//Current = CoroPull(Enumerator());
			bIsCancel = true;
		}
		virtual bool IsAlive() override
		{
			return !bIsCancel && Current;
		}

		void Resume()
		{
			Current();
		}

		YieldInstruction* GetYieldReturn()
		{
			return Current.get();
		}
	};
}
